package com.xmg.p2p.business.service.impl;

import com.xmg.p2p.base.domain.Account;
import com.xmg.p2p.base.domain.Userinfo;
import com.xmg.p2p.base.query.PageResult;
import com.xmg.p2p.base.service.AccountService;
import com.xmg.p2p.base.service.UserinfoService;
import com.xmg.p2p.base.util.BidConst;
import com.xmg.p2p.base.util.BitStatesUtils;
import com.xmg.p2p.base.util.UserContext;
import com.xmg.p2p.business.domain.*;
import com.xmg.p2p.business.mapper.*;
import com.xmg.p2p.business.query.BidRequestQueryObject;
import com.xmg.p2p.business.query.PaymentScheduleQueryObject;
import com.xmg.p2p.business.service.IAccountFlowService;
import com.xmg.p2p.business.service.IBidRequestService;
import com.xmg.p2p.business.service.SystemAccountService;
import com.xmg.p2p.business.util.CalculatetUtil;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

@Service
public class BidRequestServiceImpl implements IBidRequestService {

    @Autowired
    private BidRequestMapper bidRequestMapper ;
    @Autowired
    private UserinfoService userinfoService;
    @Autowired
    private AccountService accountService;
    @Autowired
    private BidRequestAuditHistoryMapper bidRequestAuditHistoryMapper;
    @Autowired
    private BidMapper bidMapper;
    @Autowired
    private IAccountFlowService iAccountFlowService;
    @Autowired
    private SystemAccountService systemAccountService;
    @Autowired
    private PaymentScheduleMapper paymentScheduleMapper;
    @Autowired
    private PaymentScheduleDetailMapper paymentScheduleDetailMapper;

    /**
     *乐观锁的控制
     */
    public void update(BidRequest bidRequest) {
        int count = this.bidRequestMapper.updateByPrimaryKey(bidRequest) ;
        if (count <= 0) {
            throw new RuntimeException("乐观锁错误bidRequest:" +bidRequest.getId());
        }
    }

    /**
     * 判断当前用户是否可以申请借款
     */
    public boolean canApply(Userinfo userinfo) {
        //判断实名 视频 基本资料 风控材料
        return userinfo.getIsRealAuth() && userinfo.getIsVedioAuth()
                && userinfo.getIsBasicInfo()
                && userinfo.getAuthScore() >= BidConst.CREDIT_BORROW_SCORE;
    }

    /**
     * 进行提交审核
     * @param
     */
    @Override
    public void apply(BidRequest br) {
        //完善借款对象信息
        //修改表的状态 -- 待发布状态
        //添加用户对象状态 -- 添加一个有借款在审核状态

        //得到当前的用户
        Userinfo current = this.userinfoService.getById(UserContext.getCurrent().getId());
        //账户对象
        Account account = this.accountService.getById(UserContext.getCurrent().getId()) ; //剩余信用额度等信息会用到
        //判断用户是否可以借款
        if (this.canApply(current)  //表示当前用户满足借款的基本条件
                //是否有借款在审核流程中
                &&  !current.getHasBidRequestInProcess() //没有借款在审核流程中
                //compareTo() 方法用于将 Number 对象与方法的参数进行比较.
                //指定的数与参数相等返回0, 小于参数返回-1, 大于参数返回1
                //系统最小的借款金额<= 借款金额<=剩余信用额度
                &&  br.getBidRequestAmount().compareTo(BidConst.SMALLEST_BIDREQUEST_AMOUNT) >= 0 //剩余未投满金额 >= 最小借款金额
                &&  br.getBidRequestAmount().compareTo(account.getRemainBorrowLimit()) <= 0 //剩余未投满金额 <= 账户剩余授信额度
                //系统最低年化率<= 年化率 <= 系统最高年化率
                &&  br.getCurrentRate().compareTo(BidConst.SMALLEST_CURRENT_RATE) >= 0
                &&  br.getCurrentRate().compareTo(BidConst.MAX_CURRENT_RATE) <= 0
                //系统最小投标 <= 最小投标金额
                &&  br.getMinBidAmount().compareTo(BidConst.SMALLEST_BID_AMOUNT) >= 0
        ) {
            //判断成功 创建一个bidrequest  设置相应的属性
            BidRequest bidRequest = new BidRequest() ;
            //给用户添加一个状态码
            bidRequest.setNote(br.getNote());
            bidRequest.setBidRequestAmount(br.getBidRequestAmount());
            bidRequest.setCurrentRate(br.getCurrentRate());
            bidRequest.setDescription(br.getDescription());
            bidRequest.setDisableDays(br.getDisableDays());
            bidRequest.setMinBidAmount(br.getMinBidAmount());
            bidRequest.setMonthes2Return(br.getMonthes2Return());
            bidRequest.setReturnType(br.getReturnType());
            bidRequest.setTitle(br.getTitle());
            //设置借款类型 :信用标    申请时间   申请人
            bidRequest.setBidRequestType(BidConst.BIDREQUEST_TYPE_NORMAL);//设置借款类型为信用标
            bidRequest.setApplyTime(new Date());//申请时间
            bidRequest.setCreateUser(UserContext.getCurrent());//申请人
            //设置总的回报金额(总利息)    工具类中计算
            bidRequest.setTotalRewardAmount(CalculatetUtil.calTotalInterest(
                    bidRequest.getReturnType(), bidRequest.getBidRequestAmount(),
                    bidRequest.getCurrentRate(), bidRequest.getMonthes2Return()));
            //设置当前标的状态 .待发布
            bidRequest.setBidRequestState(BidConst.BIDREQUEST_STATE_PUBLISH_PENDING) ;
            this.bidRequestMapper.insert(bidRequest);
            //   给用户添加一个状态码
            current.addState(BitStatesUtils.OP_HAS_BIDREQUEST_PROCESS) ;
            this.userinfoService.update(current);
        }
    }
    /**
     * 进入审核查询列表
     */
    public PageResult query(BidRequestQueryObject qo) {
        int  count = this.bidRequestMapper.queryForCount(qo);//查询总记录数
        if (count > 0) {
            List<BidRequest> list = this.bidRequestMapper.query(qo);//获取对象集合
            return new PageResult(list, count, qo.getCurrentPage(), qo.getPageSize());//返回分页对象
        }
        return PageResult.empty(qo.getPageSize());
    }



    /**
     * 后台发标前的审核
     * 第一步：向借款审核表中插入一条审核记录
     * 第二步: 给借款对象设置审核意见
     * 第三步: 如果审核通过, 修改借款对象状态(招标中), 设置发布时间 和 到期时间
     *         如果失败, 修改借款对象状态(审核被拒绝), 移除借款人的状态码
     */
    public void publishAudit(Long id, String remark, int state) {
        //得到借款对象  ,判断状态处于发标前审核
        BidRequest br = this.bidRequestMapper.selectByPrimaryKey(id);
        if (br != null && br.getBidRequestState() ==  BidConst.BIDREQUEST_STATE_PUBLISH_PENDING) {
            //创建一个审核历史 对象  设置相关参数
            BidRequestAuditHistory h = new BidRequestAuditHistory();
            h.setApplier(br.getCreateUser()) ;//借款申请人
            h.setApplyTime(br.getApplyTime());//申请日期
            h.setAuditor(UserContext.getCurrent());//设置借款的审核人
            h.setAuditTime(new Date());//设置审核的日期
            h.setAuditType(BidRequestAuditHistory.PUBLISH_AUDIT) ; // 设置为 发标审核
            h.setRemark(remark);//设置备注
            h.setState(state); //设置审核状态
            h.setBidRequestId(br.getId());
            this.bidRequestAuditHistoryMapper.insert(h);//向bidrequestauditHistory表中插入一条数据

            br.setNote(remark); //给借款对象设置审核意见
            if (state ==BidRequestAuditHistory.STATE_AUDIT) { //表示审核通过
                // 给借款对象修改状态   修改借款信息
                br.setBidRequestState(BidConst.BIDREQUEST_STATE_BIDDING) ;// 从待发布到设置状态为招标中
                br.setPublishTime( new Date()); //设置标的发布时间
                /*这里调用的是 DateUtils系统提供的工具类中的方法
                 *  addDays(发布时间,招标天数) ---> 在 发布时间的基础上, 增加对应的天数, 就是结束时间
                 * */
                br.setDisableDate(DateUtils.addDays(br.getPublishTime(),br.getDisableDays())); //设置该标的到期时间
            }else{  //表示审核失败
                //修改借款状态
                br.setBidRequestState(BidConst.BIDREQUEST_STATE_PUBLISH_REFUSE);//设置状态为 发标审核被拒绝
                //移除状态码     借款人的状态码
                Userinfo userinfo = this.userinfoService.getById(br.getCreateUser().getId());
                userinfo.removeState(BitStatesUtils.OP_HAS_BIDREQUEST_PROCESS) ;
                this.userinfoService.update(userinfo);
            }
            this.update(br);
        }
    }
    public BidRequest getById(Long id) {
        return this.bidRequestMapper.selectByPrimaryKey(id);
    }

    /**
     *  查询一个借款相关的所有审核对象
     */
    public List<BidRequestAuditHistory> listAuditHistorayByBidRequest(Long id) {
        return this.bidRequestAuditHistoryMapper.listAuditHistorayByBidRequest(id);
    }

    public List<BidRequest> queryForList(BidRequestQueryObject qo) {
        return this.bidRequestMapper.query(qo);
    }

    @Override
    public void bid(Long bidRequestId, BigDecimal amount) {
        // 获取借款对象
        BidRequest bidRequest = this.getById(bidRequestId);
        // 得到当前账户
        Account account = this.accountService.getById(UserContext.getCurrent().getId());
        // 判断本次投标是否有效
        if (bidRequest.getBidRequestState() == BidConst.BIDREQUEST_STATE_BIDDING        // 1.借款处于招标状态
            && !account.getId().equals(bidRequest.getCreateUser().getId())      // 2.当前用户不是投标本人
            && amount.compareTo(bidRequest.getMinBidAmount())    >= 0           // 3.投标金额 >= 最小投标金额
            && amount.compareTo(bidRequest.getRemainAmount())    <= 0           // 4.投标金额 <= 当前标的剩余金额
            && amount.compareTo(account.getUsableAmount()) <= 0                 // 5.投标金额 <= 账户可用金额
        ){
            // 执行投标操作
            // 1.创建一个投标对象 设置对象的相关属性
            Bid bid = new Bid();
            bid.setActualRate(bidRequest.getCurrentRate());     // 年化利率
            bid.setAvailableAmount(amount); // 投标金额
            bid.setBidRequestId(bidRequestId);  // 对应的标对象
            bid.setBidRequestState(bidRequest.getBidRequestState());    // 标的状态
            bid.setBidRequestTitle(bidRequest.getTitle());
            bid.setBidTime(new Date());
            bid.setBidUser(UserContext.getCurrent());
            this.bidMapper.insert(bid);
            // 2.修改借款中的一些属性
            bidRequest.setBidCount(bidRequest.getBidCount()+1);
            bidRequest.setCurrentSum(bidRequest.getCurrentSum().add(amount));       // 当前已经投标金额
            // 3.减少账户的可用金额，增加冻结金额
            account.setUsableAmount(account.getUsableAmount().subtract(amount));
            account.setFreezedAmount(account.getFreezedAmount().add(amount));
            this.accountService.update(account);
            // 4.生成一条 投标流水
            this.iAccountFlowService.bidFlow(account,bid);
            // 判断是否是满标状态
            if (bidRequest.getBidRequestAmount().equals(bidRequest.getCurrentSum())){
                // 设置为满标一审的状态
                bidRequest.setBidRequestState(BidConst.BIDREQUEST_STATE_APPROVE_PENDING_1);
                // 当借款的状态发生改变的时候，需要修改所有投标的状态
                this.bidMapper.updateBidState(bidRequestId,BidConst.BIDREQUEST_STATE_APPROVE_PENDING_1);
            }
            this.update(bidRequest);
        }
    }

    @Override
    public void fullAudit1(Long id, String remark, int state) {
        // 得到当前借款对象，判断借款状态
        BidRequest bidRequest = this.getById(id);
        // 创建一个审核历史对象，设置相关对象
        BidRequestAuditHistory hs = new BidRequestAuditHistory();
        hs.setApplier(bidRequest.getCreateUser());
        hs.setApplyTime(new Date());
        hs.setAuditor(UserContext.getCurrent());
        hs.setAuditTime(new Date());
        hs.setAuditType(BidRequestAuditHistory.FULL_AUDIT1);
        hs.setBidRequestId(id);
        hs.setRemark(remark);
        hs.setState(state);
        this.bidRequestAuditHistoryMapper.insert(hs);

        // 判断审核状态
        if (state == BidRequestAuditHistory.STATE_AUDIT){
            // 审核通过
                // 修改标的状态 修改投标的状态
            bidRequest.setBidRequestState(BidConst.BIDREQUEST_STATE_APPROVE_PENDING_2);
            this.bidMapper.updateBidState(id,BidConst.BIDREQUEST_STATE_APPROVE_PENDING_2);
        }else{
            // 审核失败，修改表的状态的投标的状态
            bidRequest.setBidRequestState(BidConst.BIDREQUEST_STATE_REJECTED);
            this.bidMapper.updateBidState(id,BidConst.BIDREQUEST_STATE_REJECTED);
            returnMoney(bidRequest);
            Userinfo borrowUserinfo = this.userinfoService.getById(bidRequest.getCreateUser().getId());
            borrowUserinfo.removeState(BitStatesUtils.OP_HAS_WITHDRAW_PROCESS);
            this.userinfoService.update(borrowUserinfo);
        }
        this.update(bidRequest);
    }

    @Override
    public void fullAudit2(Long id, String remark, int state) {
        //得到当前借款对象 判断状态
        BidRequest bidRequest = this.getById(id);
        if (bidRequest != null &&
                bidRequest.getBidRequestState() == BidConst.BIDREQUEST_STATE_APPROVE_PENDING_2){
            //创建一个审核历史对象 设置值
            BidRequestAuditHistory hs = new BidRequestAuditHistory();
            hs.setApplier(bidRequest.getCreateUser());  // 设置借款人
            hs.setApplyTime(new Date());    // 设置申请时间
            hs.setAuditor(UserContext.getCurrent());    // 设置审核人
            hs.setAuditTime(new Date());        // 设置审核时间
            hs.setAuditType(BidRequestAuditHistory.FULL_AUDIT2);    // 设置为满标二审
            hs.setBidRequestId(id);     // 设置外键，借款人的ID
            hs.setRemark(remark);       // 设置备注信息
            hs.setState(state);         // 设置二审的审核状态
            this.bidRequestAuditHistoryMapper.insert(hs);   // 添加二审的审核信息到数据库中

            if (state == BidRequestAuditHistory.STATE_AUDIT) {   // 审核通过
                // 1.从借款人的角度来
                bidRequest.setBidRequestState(BidConst.BIDREQUEST_STATE_PAYING_BACK);//*1修改借款的状态成还款中
                this.bidMapper.updateBidState(id, BidConst.BIDREQUEST_STATE_PAYING_BACK); //*2修改投标的状态
                //*1.借款人账户余额增加, 生成同一条账户流水
                // 根据借款对象的ID获取用户对象
                Account borrowAccount = this.accountService.getById(bidRequest.getCreateUser().getId());
                // 设置借款人的用户可用余额
                borrowAccount.setUsableAmount(
                        // 在现有的余额上加上借款总金额
                        borrowAccount.getUsableAmount().add(bidRequest.getBidRequestAmount()));
                this.iAccountFlowService.borrowSuccessFlow(borrowAccount, bidRequest);

                // *3剩余信用额度减少
                borrowAccount.setRemainBorrowLimit(     // 设置信用额度
                        borrowAccount.getRemainBorrowLimit()    // 账户剩余授信额度
                                .subtract(bidRequest.getBidRequestAmount()));   // 减去借款总金额
                // *4增加unReturnAmount总额（账户代还金额）
                borrowAccount.setUnReturnAmount(            // 设置用户的代还金额
                        borrowAccount.getUnReturnAmount()  // 借款人的待还总额
                                .add(bidRequest.getTotalRewardAmount())     // 加上总利息
                                .add(bidRequest.getBidRequestAmount()));   // 加上借款总金额
                // *5支付借款手续费,生成手续费流水
                BigDecimal borrowChargeFee =
                        CalculatetUtil.calAccountManagementCharge(
                                bidRequest.getBidRequestAmount());  // 借款总金额

                // *6平台收取手续费(两个对象 --> 账户和账户流水)
                this.systemAccountService.chargeBorrowFee(borrowChargeFee, bidRequest);
                this.accountService.update(borrowAccount);      // 把借款人的用户修改后保存到数据库中

                //3从投资人角度
                //*1.遍历投标
                Map<Long, Account> updates = new HashMap<Long, Account>();
                for (Bid bid : bidRequest.getBids()) {
                    //*2减少账户的冻结金额,增加投标成功的流水
                    Long bidAccountId = bid.getBidUser().getId();
                    Account bidAccount = updates.get(bidAccountId);
                    if (bidAccount == null ) {
                        bidAccount =this.accountService.getById(bidAccountId);
                        updates.put(bidAccountId, bidAccount);
                    }
                    bidAccount.setFreezedAmount(    // 设置冻结金额
                            bidAccount.getFreezedAmount().  // 当前冻结金额减去借款总金额
                                    subtract(bid.getAvailableAmount()));
                    this.iAccountFlowService.bidSuccessFlow(bidAccount,bid);
                }
                //4满标审核后的还款流程 : 生成针对这个借款的还款信息和回款信息
                List<PaymentSchedule> pss = createPaymentSchedule(bidRequest);
                //*3增加待收利息和待收本金
                //遍历还款对象和回款对象
                for (PaymentSchedule ps : pss) {
                    for (PaymentScheduleDetail psd : ps.getPaymentScheduleDetails()) {
                        Account bidAccount = updates.get(psd.getToLogininfoId()); //得到收款人的账户
                        //待收本金
                        bidAccount.setUnReceivePrincipal(
                                bidAccount.getUnReceivePrincipal().add(psd.getPrincipal()));
                        //待收利息
                        bidAccount.setUnReceiveInterest(
                                bidAccount.getUnReceiveInterest().add(psd.getInterest())) ;
                    }
                }
                for (Account account : updates.values()) {
                    this.accountService.update(account);
                }
            }else{
                //审核失败 (同满标一审失败)
                //*1修改标的状态
                bidRequest.setBidRequestState(BidConst.BIDREQUEST_STATE_REJECTED);
                //*2修改投标状态
                this.bidMapper.updateBidState(id, BidConst.BIDREQUEST_STATE_REJECTED);
                //*3遍历投标,每个投标人的账户可用金额增加冻结金额减少 , 创建一条取消投标流水
                this.returnMoney(bidRequest);
            }
            //*4对于借款人 移除对应的状态码(满标二审不管成功还是失败都要移除状态码)
            Userinfo borrowUserinfo = this.userinfoService.getById(bidRequest.getCreateUser().getId());
            borrowUserinfo.removeState(BitStatesUtils.OP_HAS_BIDREQUEST_PROCESS);
            this.userinfoService.update(borrowUserinfo) ;
            this.update(bidRequest);
        }
    }

    /**
     * 设置投资人的可用金额和冻结金额
     * @param bidRequest    借款信息
     */
    private void returnMoney(BidRequest bidRequest) {
        Map<Long,Account> updates = new HashMap<Long, Account>();
        for (Bid bid : bidRequest.getBids()){
            Long bidAccountId = bid.getBidUser().getId();
            Account bidAccount = updates.get(bidAccountId);
            if(bidAccount == null){
                bidAccount = this.accountService.getById(bidAccountId);
                updates.put(bidAccountId,bidAccount);
            }
            // 修改投资人账户的可用资金（将这次投标的资金加上）
            bidAccount.setUsableAmount(bidAccount.getUsableAmount().add(bid.getAvailableAmount()));
            bidAccount.setFreezedAmount(bidAccount.getFreezedAmount().subtract(bid.getAvailableAmount()));
            this.iAccountFlowService.cance1BidFlow(bidAccount,bid); // 创建一条取消投标得流水
        }
        for (Account bidAccount : updates.values()){
            this.accountService.update(bidAccount);
        }
    }

    /**
     * 创建针对这个借款的还款信息和汇款信息
     */
    private List<PaymentSchedule> createPaymentSchedule(BidRequest bidRequest) {
        List<PaymentSchedule> ret = new ArrayList<PaymentSchedule>();
        //用于 累加本金
        BigDecimal totalPrincipal = BidConst.ZERO;
        //用于累加利息
        BigDecimal totalInterest = BidConst.ZERO;
        for (int i = 0; i < bidRequest.getMonthes2Return(); i++) {
            //每期都是一个还款对象
            PaymentSchedule ps = new PaymentSchedule();
            ps.setBidRequestId(bidRequest.getId());
            ps.setBidRequestTitle(bidRequest.getTitle());
            ps.setBidRequestType(bidRequest.getBidRequestType());
            ps.setBorrowUser(bidRequest.getCreateUser());
            ps.setDeadLine(DateUtils.addMonths(bidRequest.getPublishTime(), i + 1));
            ps.setMonthIndex(i + 1);
            ps.setReturnType(bidRequest.getReturnType());
            ps.setState(BidConst.PAYMENT_STATE_NORMAL);
            if (i < bidRequest.getMonthes2Return() - 1) {
                //每期要还款的利息
                ps.setInterest(CalculatetUtil.calMonthlyInterest(bidRequest.getReturnType(),
                        bidRequest.getBidRequestAmount(),
                        bidRequest.getCurrentRate(),
                        i + 1, bidRequest.getMonthes2Return()));
                // 每期还款总金额，利息 +本金
                ps.setTotalAmount(CalculatetUtil.calMonthToReturnMoney(bidRequest.getReturnType(),
                        bidRequest.getBidRequestAmount(),
                        bidRequest.getCurrentRate(), i + 1,
                        bidRequest.getMonthes2Return()));
                //本期还款本金 总的还款 - 利息
                ps.setPrincipal(ps.getTotalAmount().subtract(ps.getInterest()));
                totalPrincipal=totalPrincipal.add(ps.getPrincipal());
                totalInterest=totalInterest.add(ps.getInterest());
            }else{
                //最后一期
                ps.setInterest(bidRequest.getTotalRewardAmount().subtract(totalInterest));
                ps.setPrincipal(bidRequest.getBidRequestAmount().subtract(totalPrincipal));
                ps.setTotalAmount(ps.getInterest().add(ps.getPrincipal())) ;
            }
            this.paymentScheduleMapper.insert(ps);
            createPaymentScheduleDetail(ps,bidRequest);
            //创建每期还款对象对应的汇款明细
            ret.add(ps);
        }
        return ret;
    }

    /**
     * 还款列表
     */
    public PageResult queryForPaymentSchedule(PaymentScheduleQueryObject qo) {
        int count = this.paymentScheduleMapper.queryForCount(qo);
        if (count > 0) {
            List<PaymentSchedule> list = this.paymentScheduleMapper.query(qo);
            return new PageResult(list, count, qo.getCurrentPage(), qo.getPageSize());
        }
        return PageResult.empty(qo.getPageSize());
    }

    @Override
    public void returnMoney(Long id) {
        //得到还款对象 判断状态
        //1处于待还款 , 并且当前是为自己的借款还款
        PaymentSchedule ps = this.paymentScheduleMapper.selectByPrimaryKey(id);
        if (ps.getState() == BidConst.PAYMENT_STATE_NORMAL
                && ps.getBorrowUser().getId().equals(UserContext.getCurrent().getId())) {
            //2借款人账户余额大于还款金额
            Account returnAccount = this.accountService.getById(ps.getBorrowUser().getId());
            if (returnAccount.getUsableAmount().compareTo(ps.getTotalAmount()) >= 0) {
            //执行还款
            //1对于还款对象,修改状态
                ps.setState(BidConst.PAYMENT_STATE_DONE);
                ps.setPayDate(new Date());
                this.paymentScheduleMapper.updateByPrimaryKey(ps);
                //2对于借款人
                //**2.1可用金额减少生成还款流水
                returnAccount.setUsableAmount(returnAccount.getUsableAmount().subtract(ps.getTotalAmount()));
                this.iAccountFlowService.returnMoneyFlow(returnAccount,ps);
                //**2.2待还金额减少 剩余信用额度增加
                returnAccount.setUnReturnAmount(returnAccount.getUnReturnAmount().subtract(ps.getTotalAmount()));
                returnAccount.setRemainBorrowLimit(returnAccount.getRemainBorrowLimit().add(ps.getPrincipal()));
                //3对于投资人
                //**3.1遍历还款明细对象
                Map<Long, Account> updates = new HashMap<Long , Account>();
                for (PaymentScheduleDetail psd : ps.getPaymentScheduleDetails()) {
                    Long bidAccountId = psd.getToLogininfoId();
                    Account bidAccount = updates.get(bidAccountId);
                    if (bidAccount == null ) {
                        bidAccount=this.accountService.getById(bidAccountId);
                        updates.put(bidAccountId, bidAccount);
                    }
                    //**3.2得到投资人对象,增加账户的可用余额,生成收款流水
                    bidAccount.setUsableAmount(bidAccount.getUsableAmount().add(psd.getTotalAmount()));
                    this.iAccountFlowService.receiveMoneyFlow(bidAccount,psd);
                    //**3.3减少待收本金和待收利息
                    bidAccount.setUnReceivePrincipal(bidAccount.getUnReceivePrincipal().subtract(psd.getPrincipal()));
                    bidAccount.setUnReceiveInterest(bidAccount.getUnReceiveInterest().subtract(psd.getInterest()));
                    //**3.4支付利息管理费,生成的支付利息管理费流水
                    BigDecimal interestChargeFee =
                            CalculatetUtil.calInterestManagerCharge(psd.getInterest());
                    bidAccount.setUsableAmount(bidAccount.getUsableAmount().subtract(interestChargeFee));
                    this.iAccountFlowService.interestChargeFeeFlow(bidAccount,interestChargeFee ,psd) ;
                    //**3.5系统账户收取利息管理费
                    this.systemAccountService.chargeInterestFee(interestChargeFee,psd) ;
                    //修改每一期回款
                    psd.setPayDate(new Date());
                    this.paymentScheduleDetailMapper.updateByPrimaryKey(psd) ;
                }
                for (Account account : updates.values()) {
                    this.accountService.update(account);
                }
                //4对于借款,如果当前还的是最后一期,借款状态变成已完成状态 ,修改投标信息
                PaymentScheduleQueryObject qo = new PaymentScheduleQueryObject();
                qo.setBidRequestId(ps.getBidRequestId());
                qo.setPageSize(-1) ;
                List<PaymentSchedule> psss = this.paymentScheduleMapper.query(qo);
                //遍历查询是否还有没有还款的还款计划
                boolean unReturn = false;
                for (PaymentSchedule temp : psss) {
                    if (temp.getState() == BidConst.PAYMENT_STATE_NORMAL
                            || temp.getState() == BidConst.PAYMENT_STATE_OVERDUE) {
                        unReturn = true ;
                        break ;
                    }
                }
                if ( !unReturn) { //说明是最后一期
                    BidRequest bidRequest = this.getById(ps.getBidRequestId());
                    bidRequest.setBidRequestState(BidConst.BIDREQUEST_STATE_COMPLETE_PAY_BACK);
                    this.update(bidRequest);
                    this.bidMapper.updateBidState(bidRequest.getId(), BidConst.BIDREQUEST_STATE_COMPLETE_PAY_BACK) ;
                }
                this.accountService.update(returnAccount);
            }
        }
    }

    /**
     * 创建针对每一期还款的回款对象
     * @param ps
     * @param bidRequest
     */
    private void createPaymentScheduleDetail(PaymentSchedule ps , BidRequest bidRequest) {
        //用于 累加 本期还款本金
        BigDecimal totalPrincipal = BidConst.ZERO;
        //用于累加 总金额 (本金+利息)
        BigDecimal totalAmount = BidConst.ZERO;
        for (int i = 0; i < bidRequest.getBids().size(); i++) {
            Bid bid = bidRequest.getBids().get(i);
            //针对每一个投标创建一个回款对象
            PaymentScheduleDetail psd = new PaymentScheduleDetail();
            psd.setBidAmount(bid.getAvailableAmount());
            psd.setBidRequestId(bidRequest.getId());
            psd.setBidId(bid.getId());
            psd.setDeadline(ps.getDeadLine());
            psd.setFromLogininfo(bidRequest.getCreateUser());
            psd.setMonthIndex(i + 1);
            psd.setToLogininfoId(bid.getBidUser().getId());
            psd.setPaymentScheduleId(ps.getId());
            psd.setReturnType(bidRequest.getReturnType());
            if (i < bidRequest.getBids().size() - 1) {
                // 回款本金 = 投标金额 / 借款金额 * 本期还款本金
                psd.setPrincipal(bid.getAvailableAmount().divide(bidRequest.getBidRequestAmount(),
                        BidConst.CAL_SCALE,
                        RoundingMode.HALF_UP).multiply(ps.getPrincipal()
                        .setScale(BidConst.CAL_SCALE,
                                RoundingMode.HALF_UP)));
                // 汇款利息 = 投标金额/ 借款金额 * 本期还款利息
                psd.setInterest(bid.getAvailableAmount().divide(bidRequest.getBidRequestAmount(),
                        BidConst.CAL_SCALE,
                        RoundingMode.HALF_UP).multiply(ps.getInterest().setScale(BidConst.CAL_SCALE,
                        RoundingMode.HALF_UP)));
                psd.setTotalAmount(psd.getInterest().add(psd.getPrincipal()));
                totalPrincipal = totalPrincipal.add(psd.getPrincipal()); //本金
                totalAmount = totalAmount.add(psd.getTotalAmount()); //本息
            } else {
                //最后一个回款明细
                psd.setPrincipal(ps.getPrincipal().subtract
                        (totalPrincipal)); //本期的剩余本金
                psd.setTotalAmount(ps.getTotalAmount().subtract(totalAmount)); //本期的剩余本息
                psd.setInterest(psd.getTotalAmount().subtract(psd.getPrincipal())); //剩余的利息
            }
            this.paymentScheduleDetailMapper.insert(psd
            );
            ps.getPaymentScheduleDetails().add(psd);
        }
    }
}
